/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __COM_UTIL_INPROC_CLASS_FACTORY_H__
#define __COM_UTIL_INPROC_CLASS_FACTORY_H__

#include <win32/win32.h>
#include <win32/registry.h>
#include <String.hpp>
#include "lock_counter.h"
#include "com_util.h"

namespace libcom
{

class InprocServer;

template<class CoClass>//template argument is used in CreateInstance() and Register() methods.
class InprocFactory : public IClassFactory
{
	//Requirements:
	
	//The CoClass class is required to have the following methods:
	//* CoClass::CoClass(InprocServer * server) - single constructor incrementing server lock count (other constructors will never be called!)
	//*	static const GUID & get_coclass_guid(), that returns coclass guid, which usually has name "CLSID_CoClass";
	//* static const String get_coclass_prog_id(), that returns coclass program id usually in the
	//  following format: "my_company.my_coclass.my_version".
	
	//Also, CoClass must implement the IUnknown interface:
	//* virtual unsigned long __stdcall AddRef();
	//* virtual unsigned long __stdcall Release();
	//* virtual long __stdcall QueryInterface(const GUID & id, void ** ptr);
	
	//This way is quite stupid, but simple;)
	//You can't inherit IUnknown implementation from multiple classes because of COM limitations.

	LockObject lock_object;	
	InprocServer * server;
	unsigned long refcount;

public:
	InprocFactory(InprocServer * srv);
	virtual ~InprocFactory();
	
	unsigned long __stdcall AddRef();
	unsigned long __stdcall Release();
	long __stdcall QueryInterface(const GUID & iid, void ** object);

	long __stdcall CreateInstance(IUnknown *, const GUID & iid, void **);
	long __stdcall LockServer(BOOL);

	static void Register(const String & server_dll_path);
};



//implementation



template<class C>
InprocFactory<C>::InprocFactory(InprocServer * srv)
	:	server(srv),
		lock_object(srv),
		refcount(0)
{
}



template<class C>
InprocFactory<C>::~InprocFactory()
{
	//util::log() << "IN InprocFactory::~InprocFactory()\r\n";
}



template<class C>
unsigned long InprocFactory<C>::AddRef()
{
    //util::log() << "IN InprocFactory::AddRef()\r\n";
	return ++refcount;
}



template<class C>
unsigned long  InprocFactory<C>::Release()
{
	//util::log() << "IN InprocFactory::Release()\r\n";
	if(--refcount > 0)
		return refcount;
	
	delete this;
	return 0;
}



template<class C>
long InprocFactory<C>::QueryInterface(const GUID & iid, void ** interface_pointer)
{
	if(iid == IID_IUnknown || iid == IID_IClassFactory)
	{
		*interface_pointer = this;
		AddRef();
		return S_OK;
	}
	
	return E_NOINTERFACE;
}



template<class C>
long InprocFactory<C>::CreateInstance(IUnknown *, const GUID & iid, void ** interface_pointer)
{
	C * c = new C(server);
	
	if(S_OK != c->QueryInterface(iid, interface_pointer))
	{
		delete c;
		return E_NOINTERFACE;
	}

	return S_OK;
}



template<class C>
long InprocFactory<C>::LockServer(BOOL need_lock)
{
	if(need_lock)
		server->lock();
	else
		server->unlock();
	
	return S_OK;
}



template<class C>
void InprocFactory<C>::Register(const String & server_dll_path)
{
	//strings to write to registry
	String guid_str = libcom::guid_to_string(C::get_coclass_guid());

	//write string 1 (prog id)
	String str;
	str = "HKEY_CLASSES_ROOT\\" + C::get_coclass_prog_id() + "\\CLSID";
	win32::Registry::write(str, "", guid_str);
	
	//write string 2 (guid)
	str = "HKEY_CLASSES_ROOT\\CLSID\\" + guid_str;
	win32::Registry::write(str, "", C::get_coclass_prog_id());

	//write string 3 (path)
	str = "HKEY_CLASSES_ROOT\\CLSID\\" + guid_str + "\\InprocServer32";
	win32::Registry::write(str, "", server_dll_path);
}

}//end of namespace

#endif

